﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

using zijian666.Core;
using zijian666.Core.Abstractions;
using zijian666.DI.Abstractions;
using zijian666.DI.Features;

namespace zijian666.DI.Defaults;
/// <summary>
/// 默认的自动装配调用器
/// </summary>
public class DefaultAutowiredInvoker(ILogger<DefaultAutowiredInvoker>? logger = null) : IAutowiredInvoker
{
    private readonly ConcurrentDictionary<AutowiredDescriptor, Type> _actualTypes = [];
    private readonly ILogger<DefaultAutowiredInvoker>? _logger = logger;
    private IAutowiredFeature? _feature;

    /// <summary>
    /// 获取自动装配特性
    /// </summary>
    private IAutowiredFeature Feature => _feature ??= FeatureManager.Get<IAutowiredFeature>();

    /// <summary>
    /// 尝试自动装配
    /// </summary>
    /// <param name="context">装配上下文</param>
    /// <param name="descriptor">装配描述符</param>
    /// <param name="instance">待装配实体</param>
    /// <returns>是否装配成功</returns>
    public bool TryWired(IAutowiredContext context, AutowiredDescriptor descriptor, object instance)
    {
        if (context is null || descriptor is null || instance is null)
        {
            return true;
        }

        if (_actualTypes.TryGetValue(descriptor, out var actaulType))
        {
            return TryWired(descriptor, context, instance, actaulType);
        }

        // 通过 serviceType 获取服务, 然后尝试装配
        if (TryWired(descriptor, context, instance, descriptor.ServiceType))
        {
            return true;
        }

        if (typeof(IEnumerable).IsAssignableFrom(descriptor.FieldType))
        {
            var type = typeof(IEnumerable<>).MakeGenericType(descriptor.ServiceType);

            // 尝试获取 枚举服务  IEnumerable<_serviceType>
            if (TryWired(descriptor, context, instance, type))
            {
                _actualTypes[descriptor] = type;
                return true;
            }
            // 如果待装配类型是 IEnumerable<T> 则尝试获取 T 类型的服务
            foreach (var type1 in GetElementType(descriptor.FieldType, typeof(IEnumerable<>)))
            {
                type = typeof(IEnumerable<>).MakeGenericType(type1);
                if (TryWired(descriptor, context, instance, type))
                {
                    _actualTypes[descriptor] = type;
                    return true;
                }
            }
        }

        // 当指定了服务类型时, 不继续推断
        if (descriptor.Attribute.ServiceType is not null)
        //if (descriptor.ServiceType != descriptor.FieldType)
        {
            return false;
        }

        // 否则使用 _valueType 推测服务类型
        // 1. 尝试 循环接口 获取服务
        foreach (var type in descriptor.FieldType.GetInterfaces())
        {
            if (TryWired(descriptor, context, instance, type))
            {
                _actualTypes[descriptor] = type;
                return true;
            }
        }
        // 2. 尝试 循环基类 获取服务
        var basetype = descriptor.FieldType.BaseType;
        while (basetype is not null && basetype != typeof(object))
        {
            if (TryWired(descriptor, context, instance, basetype))
            {
                _actualTypes[descriptor] = basetype;
                return true;
            }
            basetype = basetype.BaseType;
        }
        return false;
    }

    /// <summary>
    /// 根据接收类型推断实际类型和Add方法委托
    /// </summary>
    /// <param name="fieldType">字段类型</param>
    /// <param name="list">列表对象</param>
    /// <returns>实际类型和Add方法委托</returns>
    private static (Type?, Action<object, object>?) GetListType(Type fieldType, object list)
    {
        // 忽略非 IEnumerable 的情况
        if (list is not IEnumerable || !fieldType.IsAssignableTo(typeof(IEnumerable)))
        {
            return (null, null);
        }

        // 处理不可实例化的类型
        if (fieldType.IsInterface || fieldType.IsAbstract)
        {
            foreach (var elementType in GetElementType(fieldType, typeof(ICollection<>)))
            {
                var listType = typeof(List<>).MakeGenericType(elementType);
                if (listType.IsAssignableTo(fieldType))
                {
                    return (listType, (x, y) => ((IList)x).Add(y));
                }
            }
        }

        // 处理数组
        if (fieldType.IsArray)
        {
            return (fieldType, (x, y) => ((ArrayList)x).Add(y));
        }

        // 获取集合的元素类型
        foreach (var elementType in GetElementType(list.GetType(), typeof(IEnumerable<>)))
        {
            var listType = typeof(List<>).MakeGenericType(elementType);
            if (listType.IsAssignableTo(fieldType))
            {
                return (listType, (x, y) => ((IList)x).Add(y));
            }
        }

        // 处理可实例化的对象
        if (fieldType.IsAssignableTo(typeof(IList)))
        {
            return (fieldType, (x, y) => ((IList)x).Add(y));
        }

        return (null, null);
    }

    /// <summary>
    /// 获取指定泛型接口的元素类型
    /// </summary>
    /// <param name="type">类型</param>
    /// <param name="genericTypeDefinition">泛型类型定义</param>
    /// <returns>元素类型集合</returns>
    private static IEnumerable<Type> GetElementType(Type type, Type genericTypeDefinition)
    {
        foreach (var item in type.GetInterfaces())
        {
            if (item.IsGenericType && !item.IsGenericTypeDefinition && item.GetGenericTypeDefinition() == genericTypeDefinition)
            {
                if (item.GetGenericArguments()[0] != typeof(object))
                {
                    yield return item.GetGenericArguments()[0];
                }
            }
        }
    }

    /// <summary>
    /// 尝试装配
    /// </summary>
    /// <param name="descriptor">装配描述符</param>
    /// <param name="context">装配上下文</param>
    /// <param name="instance">待装配实体</param>
    /// <param name="serviceType">服务类型</param>
    /// <returns>是否装配成功</returns>
    private bool TryWired(AutowiredDescriptor descriptor, IAutowiredContext context, object instance, Type serviceType)
    {
        var value = Feature.GetService(context, descriptor, serviceType);

        if (ReferenceEquals(typeof(void), value))
        {
            return true;
        }

        if (value is null)
        {
            if (descriptor.RequiredAttribute is not null)
            {
                var validationContext = new ValidationContext(instance)
                {
                    MemberName = descriptor.MemberName,
                    DisplayName = descriptor.DisplayName
                };
                Validator.ValidateValue(null!, validationContext, [descriptor.RequiredAttribute]);
            }
            return false;
        }

        // 空数组表示赋值失败
        if (value is ICollection arr && arr.Count == 0)
        {
            return false;
        }

        // 如果类型可以匹配则直接赋值
        if (descriptor.FieldType.IsInstanceOfType(value))
        {
            descriptor.SetValue(context, instance, value);
            return true;
        }

        // 获取集合类型和Add方法
        var (listType, add) = GetListType(descriptor.FieldType, value!);
        if (listType is null || add is null)
        {
            return false;
        }

        try
        {
            var list = new List(ActivatorUtilities.CreateInstance(context, listType), add);
            foreach (var item in (IEnumerable)value!)
            {
                list.Add(item);
            }
            if (list.Count == 0)
            {
                return false;
            }
            if (descriptor.FieldType.IsArray)
            {
                descriptor.SetValue(context, instance, list.ToArray(descriptor.FieldType.GetElementType()!));
            }
            else
            {
                descriptor.SetValue(context, instance, list.Instance);
            }
            return true;
        }
        catch (Exception e)
        {
            // 处理异常
        }

        // 尝试获取 类型转换组件
        var conv = FeatureManager.Get<IFormatterConverterFeature>();
        if (conv is not null && conv.TryConvert(value, descriptor.FieldType, out var outValue))
        {
            descriptor.SetValue(context, instance, outValue);
            return true;
        }
        return false;
    }

    /// <summary>
    /// 列表结构体
    /// </summary>
    private struct List
    {
        public List(object instance, Action<object, object> add)
        {
            _instance = instance;
            _add = add;
        }

        private readonly object _instance;

        private readonly Action<object, object> _add;

        public object Instance => _instance;

        public object ToArray(Type elementType)
            => ((ArrayList)_instance).ToArray(elementType);

        public void Add(object item)
        {
            _add(_instance, item);
            Count++;
        }

        public int Count { get; private set; }
    }
}
